// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <list>
#include <string>
#include <utility>
#include <vector>

#include "coin2/exchange/base/api_base/asio_http_context.h"
#include "coin2/exchange/base/api_base/base_auth.h"
#include "coin2/exchange/base/api_util/auth_key.h"
#include "coin2/exchange/base/api_util/url_params_builder.h"

class UpbitBaseAuth {
 public:
  using HeaderSet = std::vector<std::pair<std::string, std::string>>;

  UpbitBaseAuth() {
    header_set_.push_back(std::make_pair("Authorization", ""));
    header_set_.push_back(std::make_pair("Content-type", "application/json"));
    header_set_.push_back(std::make_pair("Cache-Control", "no-cache"));
  }

  ~UpbitBaseAuth() = default;
  UpbitBaseAuth(const UpbitBaseAuth&) = delete;
  UpbitBaseAuth& operator=(const UpbitBaseAuth&) = delete;

  std::string GetHash(const std::string& query_str) const;
  std::string GetAuth(const std::string& query_str) const;
  const HeaderSet& GetHeaderSet(const std::string& query_str);

  inline void SetAccessKey(const std::string& access_key) {
    access_key_ = access_key;
    CHECK_THROW(!access_key_.empty()) << "[upbit] access key is empty";
  }
  inline void SetSecretKey(const std::string& secret_key) {
    secret_key_ = secret_key;
    CHECK_THROW(!secret_key_.empty()) << "[upbit] secret key is empty";
  }
  std::string GetExternalQueryString() const { return external_query_string_; }
  void SetExternalQueryString(const std::string& external_query_string) {
    external_query_string_ = external_query_string;
  }

 private:
  const std::string prefix_str_ = "Bearer ";
  const std::string hash_method_ = "SHA512";
  std::string access_key_;
  std::string secret_key_;
  std::string external_query_string_;
  HeaderSet header_set_;
};

class UpbitRestAuth : public UpbitBaseAuth, public BaseRestAuth {
 public:
  explicit UpbitRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      const std::string& port,
      const std::string& bind_ip)
      : key_(AuthKey::FromFile(key_filepath)), host_(host), port_(port), bind_ip_(bind_ip) {
    SetAccessKey(key_.access_key());
    SetSecretKey(key_.secret_key());
  }
  explicit UpbitRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      int32_t port,
      const std::string& bind_ip)
      : key_(AuthKey::FromFile(key_filepath)),
        host_(host),
        port_(std::to_string(port)),
        bind_ip_(bind_ip) {
    SetAccessKey(key_.access_key());
    SetSecretKey(key_.secret_key());
  }

  AsioHttpContext* CreateRequestByGetWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override;
  AsioHttpContext* CreateRequestByPostWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override;
  AsioHttpContext* CreateRequestByDeleteWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override;
  AsioHttpContext* CreateRequestByPutWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

 protected:
  AuthKey key_;
  std::string host_;
  std::string port_;
  std::string bind_ip_;
};
